package com.ht.web360.ObjServer.service;

import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ht.web360.ObjServer.dao.ObjInfo;
import com.ht.web360.ObjServer.mapper.ObjInfoMapper;
import com.ht.web360.base.dto.ObjInfo.dto.ObjInfoDto;
import com.ht.web360.base.dto.RPage;
import com.ht.web360.base.dto.UserInfo.dto.UserInfoDto;
import com.ht.web360.base.enums.ObjType;
import com.ht.web360.base.enums.UserType;
import com.ht.web360.base.feignApi.Oss;
import com.ht.web360.base.utils.RedisIdWorker;
import com.ht.web360.base.utils.ThreadLocalUtil;
import com.ht.web360.base.utils.exceptions.MessageException;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

@Service
public class ObjService {
    @Lazy
    @Autowired
    private ObjService _this;

    private final ObjInfoMapper objInfoMapper;
    private final RedisIdWorker redisIdWorker;
    private final Oss oss;

    public ObjService(ObjInfoMapper objInfoMapper, RedisIdWorker redisIdWorker, Oss oss) {
        this.objInfoMapper = objInfoMapper;
        this.redisIdWorker = redisIdWorker;
        this.oss = oss;
    }

    /**
     * 获取对象信息
     * @param id 对象信息id
     * @return 对象信息传输对象
     */
    @Cacheable(cacheNames = "objInfo", key = "#p0")
    public ObjInfoDto getObjInfoById(long id) {
        ObjInfo objInfo = objInfoMapper.selectById(id);
        if (objInfo == null) {
            return null;
        }
        return objInfo.toDto();
    }

    /**
     * 分页查询对象数据
     * @param current 当前页码
     * @param size 当前页大小
     * @return 分页对象数据，不包含对象链接
     */
    public RPage<ObjInfoDto> pageObjInfo(long current, long size) {
        var query = new LambdaQueryChainWrapper<>(ObjInfo.class);
        UserInfoDto user = ThreadLocalUtil.getUserDto();
        if (user.getUserType() == UserType.User) { // 普通用户只能查到自身拥有的对象
            query.eq(ObjInfo::getCreatorId, ThreadLocalUtil.getUserId());
        }
        Page<ObjInfo> page = new Page<>(current, size);
        objInfoMapper.selectPage(page, query.getWrapper());
        return RPage.getInstance(page, ObjInfo::toDto);
    }

    /**
     * 上传对象
     * @return 对象信息
     */
    @Transactional
    public ObjInfoDto uploadObj(String objName, String objType, MultipartFile file) {
        ObjType type = ObjType.valueOf(objType);
        String name;
        if (StrUtil.isBlankIfStr(objName)) {
            name = file.getOriginalFilename();
        } else {
            name = URLDecoder.decode(objName, StandardCharsets.UTF_8);
        }

        ObjInfo objInfo = new ObjInfo();
        objInfo.setId(redisIdWorker.nextId());
        objInfo.setName(name);
        objInfo.setType(type);
        objInfo.setCreatorId(ThreadLocalUtil.getUserId());

        switch (type) {
            case RectangularJpeg:
            case RectangularPng:
            {
                doRectangularImg(objInfo, file);
                break;
            }
            case ImageJpeg:
            case ImagePng:
            {
                doImage(objInfo, file);
                break;
            }
            default: {
                doDefault(objInfo, file);
            }
        }
        return objInfo.toDto();
    }

    /**
     * 下载对象
     * @param objLink 对象链接
     * @return 对象的数据
     */
    public InputStream downloadObj(String objLink) {
        return oss.download(objLink);
    }

    /**
     * 更新对象信息
     * @param objInfoDto 对象信息（需要包含id）
     */
    @Transactional
    @CacheEvict(cacheNames = "objInfo", key = "#p0.id")
    public void updateObj(ObjInfoDto objInfoDto) {
        if (objInfoDto == null || objInfoDto.getId() == null || !NumberUtil.isLong(objInfoDto.getId())) {
            throw new MessageException("参数错误，对象id为空");
        }
        ObjInfo objInfo = ObjInfo.fromDto(objInfoDto);
        UserInfoDto userDto = ThreadLocalUtil.getUserDto();
        if (userDto.getUserType() != UserType.Admin && !Objects.equals(objInfoDto.getCreatorId(), userDto.getId())) {
            throw new MessageException("只能修改自己的数据");
        }
        objInfo.setUpdateTime(null);
        objInfo.setCreateTime(null);
        objInfo.setLink(null);
        objInfo.setPreview(null);
        objInfo.setCreatorId(null);
        objInfoMapper.updateById(objInfo);
    }

    /**
     * 删除对象
     * @param id 对象id
     */
    @Transactional
    @CacheEvict(cacheNames = "objInfo", key = "#p0")
    public void deleteObj(long id) {
        ObjInfoDto objInfoDto = _this.getObjInfoById(id);
        UserInfoDto userInfoDto = ThreadLocalUtil.getUserDto();
        if (objInfoDto == null) {
            throw new MessageException("删除对象不存在");
        }
        if (!Objects.equals(objInfoDto.getCreatorId(), userInfoDto.getId())) {
            throw new MessageException("只能删除自己上传的文件");
        }
        objInfoMapper.deleteById(id);
        long count = countObjLink(objInfoDto.getLink());
        // todo 校验是否有poj引用对象
        if (count == 0) {
            oss.delete(objInfoDto.getLink());
            oss.delete(objInfoDto.getPreview());
        }
    }

    public long countObjLink(String link) {
        if (StrUtil.isBlankIfStr(link)) {
            throw new IllegalArgumentException("参数错误");
        }
        var query = new LambdaQueryChainWrapper<>(ObjInfo.class);
        query.eq(ObjInfo::getLink, link);
        return objInfoMapper.selectCount(query.getWrapper());
    }

/****************************************************************************************************/

    // 全景图处理
    private void doRectangularImg(ObjInfo objInfo, MultipartFile file) {
        try {
            byte[] data = IoUtil.readBytes(file.getInputStream());
            String link = oss.upload(data);
            objInfo.setLink(link);
            ThreadUtil.execute(() -> {
                String preview;
                if (data.length > 10 * 1024 * 1024) {
                    FastByteArrayOutputStream os = new FastByteArrayOutputStream();
                    try {
                        Thumbnails.of(new ByteArrayInputStream(data))
                                .size(1000, 1000)
                                .toOutputStream(os);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    preview = oss.upload(os.toByteArray());
                } else {
                    preview = link;
                }
                objInfo.setPreview(preview);
                objInfoMapper.insert(objInfo);
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 普通图片处理
    private void doImage(ObjInfo objInfo, MultipartFile file) {
        this.doDefault(objInfo, file);
    }

    // 默认对象处理
    private void doDefault(ObjInfo objInfo, MultipartFile file) {
        try {
            byte[] data = IoUtil.readBytes(file.getInputStream());
            String link = oss.upload(data);
            objInfo.setLink(link);
            objInfo.setPreview(link);
            objInfoMapper.insert(objInfo);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
